# 与comment评论相关视图

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated

# models
from sa.models import *
# serializer
from sa.serializers.CommentSerializer import CommentSerializer

import logging

# 日志记录对象
logger = logging.getLogger('django')


class CommentView(GenericAPIView):
    """ 评论视图集，支持评论的查看和新增 """
    serializer_class = CommentSerializer
    # select_related() 用于外键（ForeignKey）或一对一（OneToOne）关系的查询
    # prefetch_related() 用于优化多对多（ManyToMany）或反向外键关系
    # 使用双下划线 __ 表示跨表关联 父评论的作者（Comment.parent是外键，parent.author是嵌套外键）
    # 先通过 parent 字段关联到父评论，再通过父评论的 author 字段关联到父评论的作者
    queryset = Comment.objects.all().select_related(
        'article', 'author', 'parent__author'
    ).prefetch_related('comment_set').order_by('-id')
    permission_classes = [IsAuthenticated]

    """
        # 字典推导式,将视图得到的查询集创建为评论字典，键为评论ID，值为评论对象 和 子评论列表
        {
            1:{'comment': 评论对象1},
            2:{'comment': 评论对象2},
            3:{'comment': 评论对象3},
        }
        重点知识：
        如果不使用字典，每次查找父评论需要遍历整个列表（O (n)），处理 n 条子评论的总时间就是 O (n²)
        使用字典后，构建字典需要 O (n)，但每次查找只需 O (1)，处理 n 条子评论的总时间是 O (n)
        当评论数量很大时（比如 1 万条），O (n²) 需要执行 1 亿 次操作，而 O (n) 只需要 2 万 次操作，性能差距是 5000 倍！
        通过使用字典，我们用一次 O (n) 的预处理，换取了后续所有查找操作的 O (1) 效率，
        这是算法优化中非常经典的 "用空间换时间" 策略
        关键结论总结
        1。for 循环遍历是 O (n)：
            对查询集执行一次 for 循环（构建字典）的时间复杂度是 O (n)，运行时间与数据量成正比
        2。字典查找是 O (1)：
            字典基于哈希表实现，无论数据量多大，查找操作的时间都是恒定的
        3。空间换时间策略：
            先花 O (n) 时间构建字典（空间开销 O (n)）
            后续每次查找操作从 O (n) 降为 O (1)，整体时间复杂度从 O (n²) 优化到 O (n)
        """

    def build_comment_tree(self, comments):
        """
        创建类方法将扁平的评论列表转换为树形结构
        :param comments: 评论查询集或列表
        :return: 树形结构评论列表
        """

        # 创建评论字典：{评论ID: 评论对象}
        # 这样可以通过ID快速找到任何评论对象
        comment_dict = {
            comment_obj.id: comment_obj for comment_obj in comments  # 字典推导式
        }
        # 初始化最终返回的树形结构根评论列表
        tree_comments = []
        # 构建树形结构
        for comment_obj in comments:
            if comment_obj.parent is None:  # 根评论，放到tree_comments里，root层
                tree_comments.append(comment_obj)
            else:
                # 如果是子评论，找到它的父评论
                parent_id = comment_obj.parent.id
                # 如果id 也在评论字典 comment_dict 里, 检查父评论是否存在（防御性编程）
                # 确保父评论存在：数据库中可能存在数据不一致的情况（比如父评论被删除但子评论还在）
                # 如果父评论不存在，直接访问会抛出KeyError
                if parent_id in comment_dict:  # 字典使用 in 时，Python 默认检查的是字典的键（keys），而不是值（values）
                    # 得到父评论对象
                    parent_obj = comment_dict[parent_id]
                    # 确保父评论有replies属性（用于存储子评论）
                    if not hasattr(parent_obj, 'replies'):
                        parent_obj.replies = []
                    # 将当前评论添加到父评论的replies列表中
                    parent_obj.replies.append(comment_obj)
                else:
                    # 父评论不存在时的处理（例如记录日志,作为根评论）
                    logger.warning(f"Comment {comment_obj.id} has non-existent parent {parent_id}, 将保存为根评论！")
                    tree_comments.append(comment_obj)
        return tree_comments

    def get_queryset(self):
        """ 重写 self.get_queryset() 增加文章id过滤 """
        if 'article_pk' not in self.kwargs:  # 从 URL 路由中捕获的命名参数
            # 如果没有article_pk, 直接返回空查询集
            return self.queryset.none()
        article_pk = self.kwargs['article_pk']
        return super().get_queryset().filter(article_id=article_pk)

    def get(self, request, article_pk):
        # 获取文章下的所有评论个数统计（平铺结构）
        comments = self.get_queryset()
        # 调用类方法，生成树状结构的评论
        tree = self.build_comment_tree(comments)
        serializer = self.get_serializer(instance=tree, many=True)
        return Response(data=serializer.data)

    def post(self, request, article_pk):
        try:
            article = Article.objects.get(pk=article_pk)
        except Article.DoesNotExist:
            return Response(
                data={'error': f'Article with ID {article_pk} not found'},
                status=status.HTTP_404_NOT_FOUND
            )
        comment_serializer = self.get_serializer(data=request.data)
        comment_serializer.is_valid(raise_exception=True)

        # 处理父评论 和 深度 depth
        # 初始化一个变量，用于存储父评论对象，默认值为 None（表示没有父评论）
        parent_comment = None
        # 从客户端提交的数据 (request.data) 中尝试获取 parent 字段的值
        # 序列化器 parent 使用 PrimaryKeyRelatedField， 肯定是传入的 id
        # .get('parent')：安全地获取 parent 字段的值（如果字段不存在，返回 None，而不会报错）。
        # 如果是顶级评论（没有 parent 字段），parent_id 为 None
        # 如果是回复评论，parent_id 是父评论的数据库主键（例如 42）
        parent_id = request.data.get('parent')
        if parent_id:
            try:
                # 根据id 得到父评论对象
                parent_comment = Comment.objects.get(pk=parent_id)

                # 校验父评必须同一个article文章
                if parent_comment.article != article:
                    logger.warning(f"父评论不属于本文章: {parent_id}")
                    return Response(
                        {'error': '父评论不属于本文章'},
                        status=status.HTTP_400_BAD_REQUEST
                    )

                # 深度等父评论的深度+1
                depth = parent_comment.depth + 1
            except Comment.DoesNotExist:
                return Response(
                    data={'error': f'Parent comment with ID {parent_id} not found'},
                    status=status.HTTP_404_NOT_FOUND
                )
        else:
            depth = 0

        # 序列化器完成校验后，再增加必填内容
        comment_serializer.save(
            article=article,
            author=request.user,
            depth=depth
        )
        logger.info(f"Comment: {request.user}, 提交了评论")
        return Response(comment_serializer.data, status=status.HTTP_201_CREATED)


class CommentDetailView(GenericAPIView):
    """ 评论视图集，支持评论的删除 """
    serializer_class = CommentSerializer
    queryset = Comment.objects.all().select_related(
        'article', 'author', 'parent__author'
    )
    permission_classes = [IsAuthenticated]

    def delete(self, request, pk):
        try:
            instance = self.get_object()
        except Comment.DoesNotExist:
            return Response(
                data={'error': f'Comment with ID {pk} not found'},
                status=status.HTTP_404_NOT_FOUND
            )
        if instance.author != request.user:
            return Response(
                data={'error': '只有创建者才能删除评论'},
                status=status.HTTP_403_FORBIDDEN
            )
        instance.delete()
        logger.info(f"Comment: {request.user}, 删除了ID为 {pk}的评论")
        return Response(status=status.HTTP_204_NO_CONTENT)
